package fun.anin.aninhongbao.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import fun.anin.aninhongbao.dto.RedPacketRedisModel;
import fun.anin.aninhongbao.dto.SendRedPacketDTO;
import fun.anin.aninhongbao.dto.SnatchRedPacketDTO;
import fun.anin.aninhongbao.dto.WalletDTO;
import fun.anin.aninhongbao.entity.RedPacketInfo;
import fun.anin.aninhongbao.dao.RedPacketInfoDao;
import fun.anin.aninhongbao.entity.RedPacketRecord;
import fun.anin.aninhongbao.result.R;
import fun.anin.aninhongbao.sender.RabbitMQUtil;
import fun.anin.aninhongbao.service.RedPacketInfoService;
import fun.anin.aninhongbao.service.RedPacketRecordService;
import fun.anin.aninhongbao.service.WalletService;
import fun.anin.aninhongbao.util.GrapRedEnvelope;
import fun.anin.aninhongbao.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * 红包信息表(RedPacketInfo)表服务实现类
 *
 * @author makejava
 * @since 2020-03-29 14:38:28
 */
@Service("redPacketInfoService")
public class RedPacketInfoServiceImpl extends ServiceImpl<RedPacketInfoDao,RedPacketInfo> implements RedPacketInfoService {

    /**
     * 分布式锁
     */
    private static final String RED_PACKET_LOCK = "redPacket";
    /**
     * 抢红包记录前缀
     */
    private static final String RED_PACKET_RECORD_PREFIX = "record_";
    /**
     * 红包过期时间
     */
//    private static final int BACK_TIME = 1000*60*60*24;
    private static final int BACK_TIME = 1000*60*60;
    @Resource
    private RedPacketInfoDao redPacketInfoDao;
    @Autowired
    private WalletService walletService;
    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RabbitMQUtil rabbitMQUtil;

    @Override
    @Transactional
    public R sendRedPacket(SendRedPacketDTO dto) {
        //扣钱
        WalletDTO walletDTO = WalletDTO.builder()
                .userId(dto.getUserId())
                .toUserId(0)
                .money(dto.getMoney())
                .type(2)
                .msg(dto.getType()==1?"发送普通红包":"发送拼手气红包")
                .build();
        R r = walletService.updateMoney(walletDTO);

        if(r.getCode()==200){
            //新增发送红包记录
            RedPacketInfo redPacketInfo = RedPacketInfo.builder()
                    .totalPacket(dto.getRedPacketNum())
                    .totalAmount(dto.getMoney())
                    .type(dto.getType())
                    .userId(dto.getUserId())
                    .version(1)
                    .build();
            redPacketInfoDao.insert(redPacketInfo);

            //将红包信息缓存至redis
            RedPacketRedisModel redisModel = RedPacketRedisModel.builder()
                    .userId(dto.getUserId())
                    .redPacketId(redPacketInfo.getId())
                    .surplusMoney(dto.getMoney())
                    .surplusNum(dto.getRedPacketNum())
                    .type(dto.getType())
                    .datetime(System.currentTimeMillis())
                    .build();
            redisUtil.set(redPacketInfo.getId().toString(),redisModel);

            //向mq发送红包失效退款的消息
            rabbitMQUtil.sendLazy("redpacket.BackExchange","redpacket.back",BACK_TIME,redPacketInfo);
        }
        return r;
    }

    @Override
    public R snatchRedPacket(SnatchRedPacketDTO dto) {
        RedPacketRedisModel redisModel = (RedPacketRedisModel) redisUtil.get(dto.getRedPacketId().toString());
        if(redisModel != null){
            if(redisModel.getSurplusNum()>0){
                return R.ok();
            }
        }
        return R.ok("手慢了~红包被抢完了");
    }

    @Override
    @Transactional
    public R openRedPacket(SnatchRedPacketDTO dto) {

        try{
            //先查询红包还有剩余没
            RedPacketRedisModel redPacketRedisModel = (RedPacketRedisModel) redisUtil.get(dto.getRedPacketId().toString());

            if(redPacketRedisModel != null){
                if( redPacketRedisModel.getSurplusNum() > 0){
                    int surplusNum = redPacketRedisModel.getSurplusNum();

                    //判断这个人抢过这个红包没有
                    if(redisUtil.rank(RED_PACKET_RECORD_PREFIX+dto.getRedPacketId().toString(),dto.getUserId()) != null){
                        return R.ok("您已经抢过这个红包了");
                    }

                    //尝试获得锁
                    boolean isLock = redisUtil.getLock(RED_PACKET_LOCK);
                    if(isLock){
                        //并判断红包的个数
                        redPacketRedisModel = (RedPacketRedisModel) redisUtil.get(dto.getRedPacketId().toString());
                        surplusNum = redPacketRedisModel.getSurplusNum();

                        if(surplusNum > 0){
                            int surplusMoney = redPacketRedisModel.getSurplusMoney();

                            //缓存红包减一
                            surplusNum --;

                            //判断红包类型 计算红包金额
                            int money = 0;
                            String msg = null;
                            if(redPacketRedisModel.getType() == 1){
                                money = GrapRedEnvelope.getMoney(redPacketRedisModel.getSurplusMoney(),redPacketRedisModel.getSurplusNum());
                                msg = "抢到普通红包";
                            }else if(redPacketRedisModel.getType() == 2){
                                money = GrapRedEnvelope.getMathMoney(redPacketRedisModel.getSurplusMoney(),redPacketRedisModel.getSurplusNum());
                                msg = "抢到拼手气红包";
                            }
                            //将抢红包的记录存入zset
                            redisUtil.setZset(RED_PACKET_RECORD_PREFIX+dto.getRedPacketId().toString(),dto.getUserId(),money);

                            //异步记录
                            RedPacketRecord record = RedPacketRecord.builder()
                                    .amount(money)
                                    .redPacketId(redPacketRedisModel.getRedPacketId())
                                    .uid(dto.getUserId())
                                    .build();
                            rabbitMQUtil.send("redpacket.RecordExchange","redpacket.record",record);

                            //异步到账
                            WalletDTO walletDTO = WalletDTO.builder()
                                    .userId(dto.getUserId())
                                    .toUserId(redPacketRedisModel.getUserId())
                                    .money(money)
                                    .type(1)
                                    .msg(msg)
                                    .build();
                            rabbitMQUtil.send("redpacket.arrival",walletDTO);

                            //如果是最后一个红包 则需要删除缓存
                            if(surplusNum == 0){
                                //如果是拼手气红包，直接产生运气王
                                if (redPacketRedisModel.getType() == 2){
                                    Set set = redisUtil.reverseRange(RED_PACKET_RECORD_PREFIX+dto.getRedPacketId().toString(),0,0);
                                    Long uid = Long.parseLong(set.iterator().next().toString());

                                    //socket推送运气王消息
                                    double time = (System.currentTimeMillis()-redPacketRedisModel.getDatetime())/1000;
                                    System.err.println(dto.getRedPacketId() + "红包已被抢光，用时" + time + "秒，运气王是" + uid);

                                    //异步修改数据库标识
                                    record = RedPacketRecord.builder()
                                            .redPacketId(dto.getRedPacketId())
                                            .uid(uid)
                                            .build();
                                    rabbitMQUtil.send("redpacket.markAsLuck",record);
                                }
                                //删除缓存 完成
                                redisUtil.del(dto.getRedPacketId().toString(),
                                        RED_PACKET_RECORD_PREFIX+dto.getRedPacketId(),
                                        RedisUtil.LOCK_PREFIX+dto.getRedPacketId());
                            }else{
                                //更新缓存红包金额
                                redPacketRedisModel.setSurplusNum(surplusNum);
                                redPacketRedisModel.setSurplusMoney(surplusMoney - money);
                                redisUtil.set(dto.getRedPacketId().toString(),redPacketRedisModel);
                            }

                            //释放锁
                            redisUtil.del(RED_PACKET_LOCK);
                            return R.ok(money);
                        }
                    }else{
                        return R.ok("这个红包太烫手了~歇一秒再抢吧");
                    }
                }
                return R.ok("手慢了~红包被抢完了");
            }else {
                return R.ok("手慢了~红包已经过期了");
            }
        }catch (Exception e){
            //如果发生异常则释放锁
            redisUtil.del(RED_PACKET_LOCK);
            log.error("打开红包异常",e);
            return R.error();
        }

    }

    @Override
    public R backRedPacket(RedPacketInfo redPacketInfo) {
        //查询红包还有多少余额
        RedPacketRedisModel redPacketRedisModel = (RedPacketRedisModel)redisUtil.get(redPacketInfo.getId().toString());
        if(redPacketRedisModel != null && redPacketRedisModel.getSurplusNum() > 0){

            //删除缓存红包的信息
            redisUtil.del(redPacketInfo.getId().toString(),
                    RED_PACKET_RECORD_PREFIX + redPacketInfo.getId().toString(),
                    RedisUtil.LOCK_PREFIX + redPacketInfo.getId().toString());

            String msg = null;
            if(redPacketInfo.getType() ==1 ){
                msg = "普通红包退款";
            }else if(redPacketInfo.getType() ==2){
                msg = "拼手气红包退款";
            }

            WalletDTO walletDTO = WalletDTO.builder()
                    .userId(redPacketInfo.getUserId())
                    .money(redPacketRedisModel.getSurplusMoney())
                    .type(1)
                    .msg(msg)
                    .build();
            walletService.updateMoney(walletDTO);
        }
        return R.ok();
    }

    @Override
    public R simulateOpen(Long redPacketId){
        //模拟21个线程抢5个红包
        for (long i = 0; i < 21; i++) {
            long finalI = i;
            new Thread(
                    () ->{
                        for (int j = 0; j < 5; j++) {
                            SnatchRedPacketDTO dto = SnatchRedPacketDTO.builder()
                                    .redPacketId(redPacketId)
                                    .userId(finalI)
                                    .build();
                            R r = openRedPacket(dto);
                            if(r.getData() != null){
                                System.err.println("用户"+finalI+" 第"+(j+1)+"次 抢到了"+r.getData()+"分钱");
                            }else{
                                System.err.println("用户"+finalI+" 第"+(j+1)+"次 " +r.getMessage());
                            }
                            try {
                                //每次抢需要200ms
                                Thread.sleep(200);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            ).start();
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.err.println(redisUtil.get(redPacketId.toString()));
        return null;
    }
}